#pragma once

#include <vector>
#include <boost/thread.hpp>
#include <boost/unordered_set.hpp>
#include <boost/shared_ptr.hpp>
#include <boostext/exception.hpp>
#include <boostext/util/make_shared.hpp>
#include <boostext/thread/lock_ptr.hpp>

#include "task.hpp"




namespace services
{
	/**
	 * This class is a wrapper around a boost::thread object.
	 * It is designed to simply execute a (changing) list of tasks
	 * until asked to shut down.
	 */
	class thread
	{
	public:

		SHARED(thread);

	private:

		typedef boost::unordered_set<task::ptr>   tasks;

		typedef boost::shared_mutex         mutex;
		typedef boost::shared_lock<mutex>   read_lock;
		typedef boost::unique_lock<mutex>   write_lock;

		volatile bool          m_break;
		const bool             m_exclusive;
		tasks                  m_tasks;
		boost::thread          m_thread;
		mutex                  m_mutex;

	public:

		/**
		 * Spawns a new non-exclusive thread that executes all tasks that will be added to the 
		 * list of tasks.
		 */
		thread();

		/**
		 * Spawns a new exclusive thread that executes the given task.
		 */
		thread(task::ptr t);
		~thread();



		/**
		 * Tests if this thread refers to a thread-of-execution.
		 *
		 * @returns true if this thread is running
		 * @returns false otherwise
		 */
		bool joinable() const;



		/**
		 * Tests if this thread is exclusive (ie. runs only one task) or not.
		 */
		bool exclusive() const;

		/**
		 * @returns the number of tasks this thread runs
		 */
		std::size_t numTasks() const;

		/**
		 * Adds the given task to the list of tasks.
		 */
		void add(task::ptr t);

		/**
		 * Removes the given task from the list of tasks.
		 * In case the task (or any other in this thread) is being executed,
		 * the task is removed once all finished executing.
		 */
		bool remove(task::ptr t);



		/**
		 * @returns a list of all running tasks
		 */
		std::vector<task::const_ptr> activeTasks();

	private:

		void run();
	};
	///////////////////////////////////////////////////////////////////////////////////////////////
}
///////////////////////////////////////////////////////////////////////////////////////////////////
